/*
 * 创建日期 2004-10-20
 *
 * 更改所生成文件模板为
 * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
package com.ctsi.commons.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @author yueming
 * 
 *         更改所生成类型注释的模板为 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
public  class UtilFile {
	private static final Logger log = LoggerFactory.getLogger(UtilFile.class);

	public static final int BufferSize = 1024 * 8;

	public static final String Default_File_Encoding = System.getProperty("file.encoding");

	public static final String Charset_UTF8 = "UTF-8";

	public static final Charset UTF8 = StandardCharsets.UTF_8;

	public final static void charsetConvert(File infile, File outfile, Charset inCharset, Charset outCharset) throws IOException {
		if (!outfile.exists())
			outfile.createNewFile();

		RandomAccessFile inraf = null;
		RandomAccessFile outraf = null;
		FileChannel finc = null;
		FileChannel foutc = null;
		try {

			inraf = new RandomAccessFile(infile, "r");
			outraf = new RandomAccessFile(outfile, "rw");
			finc = inraf.getChannel();
			foutc = outraf.getChannel();

			MappedByteBuffer inmbb = finc.map(FileChannel.MapMode.READ_ONLY, 0, infile.length());

			CharsetDecoder inDecoder = inCharset.newDecoder();
			CharsetEncoder outEncoder = outCharset.newEncoder();

			CharBuffer cb = inDecoder.decode(inmbb);
			ByteBuffer outbb = outEncoder.encode(cb);

			foutc.write(outbb);

		} catch (IOException e) {
			throw e;
		} finally {
			UtilMisc.closeObjNoExc(finc);
			UtilMisc.closeObjNoExc(foutc);
			UtilMisc.closeObjNoExc(inraf);
			UtilMisc.closeObjNoExc(outraf);
		}

	}

	public static final void charsetConvert(File infile, File outfile, String inFileCharsetName, String outFileCharsetName) throws IOException {
		Charset inCharset = Charset.forName(inFileCharsetName);
		Charset outCharset = Charset.forName(outFileCharsetName);
		charsetConvert(infile, outfile, inCharset, outCharset);
	}

	/**
	 * 从in拷贝数据到out,注意执行完成后两者都被关闭
	 * 
	 * @param in
	 * @param out
	 * @return
	 * @throws IOException
	 */
	public static final int copy(Reader in, Writer out) throws IOException {
		assert (in != null);
		assert (in != null);
		try {
			int byteCount = 0;
			char[] buffer = new char[BufferSize];
			int bytesRead = -1;
			while ((bytesRead = in.read(buffer)) != -1) {
				out.write(buffer, 0, bytesRead);
				byteCount += bytesRead;
			}
			out.flush();
			return byteCount;
		} finally {
			try {
				in.close();
			} catch (IOException ex) {
				log.warn("Could not close Reader", ex);
			}
			try {
				out.close();
			} catch (IOException ex) {
				log.warn("Could not close Writer", ex);
			}
		}
	}

	public static final String saveTemFile(String str, String suffix) throws IOException {
		String suffix1 = (suffix == null) ? ".tem" : suffix;

		java.io.File temfile = java.io.File.createTempFile("tem", suffix1, null);
		String fileName = temfile.getAbsolutePath();
		saveToFile(str, fileName);
		return fileName;

	}

	/**
	 * 获取文件扩展名称
	 * 
	 * @param fileName
	 * @return 传入非法返回null,如果有返回小写扩展名称,没有返回空字符串
	 */
	public static final String getFileType(final String fileName) {
		if (UtilValidate.isEmpty(fileName))
			return null;
		int pos = fileName.lastIndexOf('.');
		if (pos < 0 || pos == fileName.length() - 1)
			return "";
		return fileName.substring(pos + 1).toLowerCase();

	}

	/**
	 * 获取没有扩展名的文件名称
	 * 
	 * @param fileName
	 * @return
	 */
	public static final String getFileName(String fileName) {
		if (UtilValidate.isEmpty(fileName))
			return null;
		int pos = fileName.lastIndexOf('.');
		if (pos < 0)
			return fileName;
		return fileName.substring(0, pos);

	}

	public static final void saveToFile(byte[] content, File outFile, int off, int len) throws IOException {

		OutputStream out = null;
		try {

			out = new FileOutputStream(outFile);
			if (len > 0)
				out.write(content, off, len);
			// out.flush();
		} catch (IOException e) {
			throw e;
		} catch (Throwable e) {
			throw new RuntimeException("文件写入错误。", e);
		} finally {
			UtilMisc.closeObjNoExc(out);
		}

	}

	public static final void saveToFile(byte[] content, File outFile) throws IOException {
		saveToFile(content, outFile, 0, content.length);
	}

	public static final void saveToFile(byte[] content, String fileName) throws IOException {
		File outFile = new File(fileName);
		saveToFile(content, outFile);
	}

	/**
	 * 保存文件，不会关闭InputStream
	 * 会锁定文件
	 * @param in
	 * @param outFile
	 * @throws IOException
	 */
	public static final boolean saveToFile(InputStream in, File outFile) throws IOException {
		FileOutputStream out = null;
		try {
			boolean fe = outFile.exists();

			if (!fe) {
				outFile.createNewFile();
			}
			out = new FileOutputStream(outFile);

			FileChannel fc = out.getChannel();
			FileLock fl = fc.tryLock();
			if (fl != null) {
				try {
					copyNoClose(in, out);
				} finally {
					fl.release();
				}

			} else {
				log.warn("lock file {} faile", outFile);
				return false;
			}

			return true;
		} catch (IOException e) {
			log.error("save to file {} error", outFile);
			throw e;
		} catch (Throwable e) {
			log.error("save to file {} error", outFile);
			throw new IOException("文件写入错误。", e);
		} finally {
			UtilMisc.closeObjNoExc(out);
		}
	}

	public static final boolean saveToFile(InputStream in,final String fileName) throws IOException {
		File outFile = new File(fileName);
		return saveToFile(in, outFile);
	}

	public static final void saveToFile(String str, String filename) throws IOException {
		saveToFile(str, filename, Charset_UTF8);
	}

	public static final void saveToFileDefalutEncoding(String str, String filename) throws IOException {
		saveToFile(str, filename, Default_File_Encoding);
	}

	/**
	 * 保存文件文件
	 * 
	 * @param str
	 * @param filename
	 * @param encode
	 * @throws IOException
	 */
	public static final void saveToFile(String str, String filename, String encode) throws IOException {
		File f = new java.io.File(filename);
		saveToFile(str, f, encode);
	}

	/**
	 * 保存文本文件
	 * 
	 * @param str
	 * @param f
	 * @param encode
	 * @throws IOException
	 */
	public static final void saveToFile(String str, File f, String encode) throws IOException {
		if (str == null)
			throw new IllegalArgumentException("参数is null");

		if (!f.exists())
			f.createNewFile();

		OutputStream fos = new FileOutputStream(f);

		Writer out = new BufferedWriter(new OutputStreamWriter(fos, encode));

		try {
			out.write(str);
		} catch (IOException e) {
			log.error("saveToFile error", e);
			throw e;
		} finally {
			UtilMisc.closeObjNoExc(out);
		}

	}

	public static final void delFile(String fileName) {
		java.io.File f = new java.io.File(fileName);
		if (f.exists())
			f.delete();
		else {
			log.warn("file {} no exists", fileName);
		}
	}

	public static final boolean deleteFile(String fileName) {
		java.io.File f = new java.io.File(fileName);
		if (f.exists())
			return f.delete();
		else {

			log.warn("file {} not exists", fileName);
			return true;
		}
	}

	/**
	 * 功能描述：删除字符串数组指定路径的所有文件和文件夹。
	 * 
	 * @return boolean true 删除的是文件 false是目录
	 */
	public static boolean delDirectory(String filename) {
		return deleteFileAndFolder(new File(filename.replace('\\', '/')));

	}

	/**
	 * 功能描述：删除字符串指定路径的所有文件和文件夹。
	 * 
	 * @return boolean true 文件，false 目录
	 */
	public static final boolean deleteFileAndFolder(File filename) {
		if (filename == null) {
			return false;
		}
		File file = filename;

		if (!file.exists()) {
			log.warn("file {} not exists", file.getAbsolutePath());
			return true;
		}

		if (file.isFile()) {
			return file.delete();

		} else {
			File[] allfiles = file.listFiles();
			if (UtilValidate.isNotEmpty(allfiles)) {
				for (int i = 0; i < allfiles.length; i++) {
					deleteFileAndFolder(allfiles[i]);
				}
			}

			return file.delete();

		}
	}

	public static final void deleteFolderFile(File filename) {
		if (filename == null || !filename.isDirectory()) {
			throw new RuntimeException("filename not Directory");
		}
		deleteFileAndFolder(filename);
		filename.mkdirs();
	}

	/**
	 * 清空一个目录
	 * 
	 * @param filename
	 * @return true 成功 false 是文件或不存在
	 */
	public static final boolean clearDir(File filename) {
		File file = filename;
		if (file.isFile() || !file.exists()) {

			return false;
		} else {
			File[] allfiles = file.listFiles();
			if (UtilValidate.isNotEmpty(allfiles)) {
				for (int i = 0; i < allfiles.length; i++) {
					deleteFileAndFolder(allfiles[i]);
				}
			}
			return true;
		}
	}

	public static final void copyFile(String src, String dest) throws IOException {
		FileInputStream srcStream = new FileInputStream(src);
		FileOutputStream destStream = new FileOutputStream(dest);

		copyFile(srcStream, destStream);

	}

	/**
	 * nio复制文件，关闭Stream
	 * 
	 * @param src
	 * @param dest
	 * @throws IOException
	 */
	public static final void copyFileNio(FileInputStream src, FileOutputStream dest) throws IOException {
		FileChannel sfc = src.getChannel();
		FileChannel tfc = dest.getChannel();
		ByteBuffer buff = ByteBuffer.allocate(BufferSize);
		try {

			while ((sfc.read(buff)) != -1) {
				buff.flip();
				while (buff.hasRemaining()) {
					tfc.write(buff); // 文件应该一次写入,不会出现写不完的情况,socke会
				}

				buff.clear();
			}

		} catch (IOException e) {
			throw e;
		} finally {
			tfc.close();
			sfc.close();
			dest.close();
			src.close();
		}

	}

	/**
	 * 复制文件，会关闭stream
	 * 
	 * @param srcStream
	 *            源
	 * @param destStream
	 *            目标
	 * @throws IOException
	 */
	public static final void copyFile(FileInputStream srcStream, FileOutputStream destStream) throws IOException {

		try {
			copyNoClose(srcStream, destStream);

		} finally {
			UtilMisc.closeObjNoExc(destStream);
			UtilMisc.closeObjNoExc(srcStream);

		}

	}

	public static OutputStream getBufferedOutputStream(String fileName) throws IOException {

		if (UtilValidate.isEmpty(fileName)) {
			throw new IOException("Cannot obtain buffered writer for an empty filename!");
		}

		return new BufferedOutputStream(new FileOutputStream(fileName),1024*16);
	}

	public static OutputStream getBufferedOutputStream(File f) throws IOException {

		return new BufferedOutputStream(new FileOutputStream(f),1024*16);
	}

	public static OutputStream getBufferedOutputStream(String fileName, boolean append) throws IOException {

		if (UtilValidate.isEmpty(fileName)) {
			throw new IOException("Cannot obtain buffered writer for an empty filename!");
		}

		return new BufferedOutputStream(new FileOutputStream(fileName, append),1024*16);
	}

	public static OutputStream getBufferedOutputStream(File f, boolean append) throws IOException {

		return new BufferedOutputStream(new FileOutputStream(f, append));
	}

	public static Writer getBufferedWriter(String fileName) throws IOException {

		if (UtilValidate.isEmpty(fileName)) {
			throw new IOException("Cannot obtain buffered writer for an empty filename!");
		}

		return new BufferedWriter(new FileWriter(fileName));
	}

	public static OutputStream getBufferedOutputStream(String path, String name) throws IOException {
		String fileName = getPatchedFileName(path, name);
		if (UtilValidate.isEmpty(fileName)) {
			throw new IOException("Cannot obtain buffered writer for an empty filename!");
		}

		return new BufferedOutputStream(new FileOutputStream(fileName));
	}

	public static Writer getBufferedWriter(String path, String name) throws IOException {
		String fileName = getPatchedFileName(path, name);
		if (UtilValidate.isEmpty(fileName)) {
			throw new IOException("Cannot obtain buffered writer for an empty filename!");
		}

		return new BufferedWriter(new FileWriter(fileName));
	}

	public static String getPatchedFileName(String path, String fileName) throws IOException {

		if (UtilValidate.isNotEmpty(path)) {
			path = path.replaceAll("\\\\", "/");
			File parentDir = new File(path);
			if (!parentDir.exists()) {
				if (!parentDir.mkdir()) {
					throw new IOException("Cannot create directory for path: " + path);
				}
			}

			// 构造文件名
			if (!path.endsWith("/")) {
				path = path + "/";
			}
			if (fileName.startsWith("/")) {
				fileName = fileName.substring(1);
			}
			fileName = path + fileName;
		}

		return fileName;
	}

	public static String readToString(String fileName) throws IOException {

		FileReader fr = new FileReader(fileName);

		return readToString(fr);
	}

	public static String readToString(File fileName, String charsetName) throws IOException {

		InputStreamReader fr = new InputStreamReader(new FileInputStream(fileName), charsetName);

		return readToString(fr);
	}

	/**
	 * 通过Reader读取默认编码文本文件 会按行读取，并增加分行符号
	 * 
	 * @param reader
	 *            会被关闭
	 * @return
	 * @throws IOException
	 */
	public static String readToString(Reader reader) throws IOException {
		StringBuilder tem = new StringBuilder(BufferSize);
		BufferedReader br;
		if (!(reader instanceof BufferedReader))
			br = new BufferedReader(reader);
		else
			br = (BufferedReader) reader;

		String line;
		try {
			line = br.readLine();
			final String lineSep = System.getProperty("line.separator");
			while (line != null) {
				tem.append(line).append(lineSep);
				line = br.readLine(); // 从文件中继续读取一行数据
			}
		} finally {
			br.close(); // 关闭BufferedReader对象,如果错误reader还能关闭否
			// reader.close();
		}

		return tem.toString();
	}

	public static String readToString(InputStream is, String charsetName) throws IOException {
		StringBuilder tem = new StringBuilder(getBufferSize(is));
		Reader inputReader;
		if (UtilValidate.isEmpty(charsetName))
			inputReader = new InputStreamReader(is);
		else
			inputReader = new InputStreamReader(is, charsetName);

		try {
			copyChar(inputReader, tem);
		} finally {
			inputReader.close();

		}

		return tem.toString();
	}

	public static byte[] readBytes(String fileName) throws IOException {
		if (fileName == null)
			return null;

		File f = new File(fileName);
		if (!f.exists()) {
			return null;
		}
		return readBytes(f);
	}

	private static int getBufferSize(InputStream in) throws IOException {
		int result = in.available();
		if (result < 1024)
			result = 1024;
		return result;
	}

	public static final byte[] readBytes(File f) throws IOException {

		byte[] data = null;
		InputStream in = null;
		ByteOutputStream out = null;
		try {
			in = new FileInputStream(f);
			out = new ByteOutputStream((int) f.length());
			copyNoClose(in, out);
			data = out.toByteArray();
		} catch (IOException e) {
			throw e;
		} finally {
			UtilMisc.closeObjNoExc(in);

		}
		return data;
	}

	public static byte[] readBytes(URL file) throws IOException {
		if (file == null)
			return null;
		byte[] data = null;
		InputStream in = null;
		ByteOutputStream out = null;
		try {
			in = file.openStream();
			out = new ByteOutputStream(getBufferSize(in));

			copyNoClose(in, out);
			data = out.toByteArray();
		} catch (IOException e) {
			throw e;
		} finally {
			UtilMisc.closeObjNoExc(in);

		}
		return data;
	}

	public static String readFile(String fileName, String charsetName) throws Exception {
		InputStream in = new FileInputStream(fileName);
		try {
			return readFile(in, charsetName);
		} catch (Exception e) {
			throw e;
		} finally {
			UtilMisc.closeObjNoExc(in);
		}

	}

	/**
	 * 从Reader读取,不关闭Reader
	 * 
	 * @param fr
	 * @param content
	 * @throws IOException
	 */
	public static void copyChar(Reader fr, StringBuilder content) throws IOException {
		int len = -1;
		char[] buffer = new char[BufferSize];
		while ((len = fr.read(buffer)) > -1) {
			content.append(buffer, 0, len);
		}
	}

	/**
	 * 从InputStream读取,InputStream会被关闭
	 * 
	 * @param in
	 * @param charsetName
	 * @return
	 * @throws Exception
	 */
	public static String readFile(InputStream in, String charsetName) throws Exception {

		StringBuilder content = new StringBuilder(getBufferSize(in));
		InputStreamReader fr = null;

		try {
			if (UtilValidate.isNotEmpty(charsetName))
				fr = new InputStreamReader(in, charsetName);
			else {
				log.debug("charsetName is null");
				fr = new InputStreamReader(in);
			}

			copyChar(fr, content);

		} catch (Exception e) {
			throw e;
		} finally {

			UtilMisc.closeObjNoExc(fr);
		}

		return content.toString();

	}

	/**
	 * 读取utf-8编码
	 * 
	 * @param fileName
	 * @return
	 * @throws Exception
	 */
	public static String readFile(String fileName) throws Exception {
		return readToString(new FileInputStream(fileName), Charset_UTF8);

	}

	public static void move(String input, String output) throws Exception {
		File inputFile = new File(input);
		File outputFile = new File(output);
		try {
			inputFile.renameTo(outputFile);
		} catch (Exception ex) {
			throw new Exception("Can not mv" + input + " to " + output + ex.getMessage());
		}
	}

	/**
	 * 建立指定目录
	 * 
	 * @param home
	 * @return 建立好的file
	 * @throws Exception
	 */
	public static File makeDir(String home) throws Exception {
		File homedir = new File(home);
		if (!homedir.exists()) {
			try {
				homedir.mkdirs();
			} catch (Exception ex) {
				throw new Exception("Can not mkdir :" + home + " Maybe include special charactor!");
			}
		}
		return homedir;
	}

	public static void CopyDir(String sourcedir, String destdir) throws Exception {
		CopyDir(sourcedir, destdir, true);
	}

	public static void CopyDir(String sourcedir, String destdir, boolean onErrorStop) throws Exception {
		File dest = new File(destdir);
		File source = new File(sourcedir);

		String[] files = source.list();
		try {
			makeDir(destdir);
		} catch (Exception ex) {
			log.error("make dir error", ex);
			throw new Exception("CopyDir:" + ex.getMessage());
		}

		for (int i = 0; i < files.length; i++) {
			String sourcefile = source + File.separator + files[i];
			String destfile = dest + File.separator + files[i];
			File temp = new File(sourcefile);
			try {
				if (temp.isFile()) {

					copyFile(sourcefile, destfile);

				} else if (temp.isDirectory()) {
					CopyDir(sourcefile, destfile, onErrorStop);

				}

			} catch (Throwable ex) {
				if (onErrorStop)
					throw new Exception("CopyDir:" + ex.getMessage());
				else {
					log.error("CopyDir error", ex);
				}
			}

		}
	}

	/**
	 * 保存数据
	 * 
	 * @param fileInput
	 *            数据源 不会关闭
	 * @param location
	 *            保存位置
	 * @param length
	 *            文件长度
	 * @throws IOException
	 */
	public static void saveFile(InputStream fileInput, String location, long length) throws IOException {
		OutputStream fileOut = new FileOutputStream(location);

		try {
			copy(fileInput, fileOut, length);
		} catch (Exception e) {
			throw new IOException(e);
		} finally {

			fileOut.close();
		}

	}

	/**
	 * 删除目录与之下的子目录
	 * 
	 * @param directory
	 * @throws Exception
	 */
	public static void recursiveRemoveDir(File directory) throws Exception {
		if (!directory.exists())
			throw new IOException(directory.toString() + " do not exist!");

		String[] filelist = directory.list();
		File tmpFile = null;
		for (int i = 0; i < filelist.length; i++) {
			tmpFile = new File(directory.getAbsolutePath(), filelist[i]);
			if (tmpFile.isDirectory()) {
				recursiveRemoveDir(tmpFile);
			} else if (tmpFile.isFile()) {
				try {
					tmpFile.delete();
				} catch (Exception ex) {
					throw new Exception(tmpFile.toString() + " can not be deleted " + ex.getMessage());
				}
			}
		}
		try {
			directory.delete();
		} catch (Exception ex) {
			throw new Exception(directory.toString() + " can not be deleted " + ex.getMessage());
		} finally {
			filelist = null;
		}
	}

	/**
	 * 获取目录下的所有文件，包含子目录
	 * 
	 * @param fileList
	 * @param directory
	 * @throws IOException
	 */
	public static void getFileList(List<File> fileList, File directory) throws IOException {
		getFileList(fileList, directory, null);

	}

	public static void getFileList(List<File> fileList, File directory, FilenameFilter filter) throws IOException {
		if (!directory.exists() || !directory.isDirectory())
			throw new IOException(directory.toString() + " do not exist!");

		File[] fData;
		if (filter == null)
			fData = directory.listFiles();
		else
			fData = directory.listFiles(filter);
		if (fData != null && fData.length > 0) {
			for (File f : fData) {
				if (f.isFile())
					fileList.add(f);
				else if (f.isDirectory()) {
					getFileList(fileList, f, filter);
				}
			}
		}

	}

	/**
	 * 在指定目录下查询文件
	 * 
	 * @param fileList
	 * @param path
	 * @param filter
	 * @param includeSubfolders
	 * @throws IOException
	 */
	public static void searchFiles(List<File> fileList, File path, FilenameFilter filter, boolean includeSubfolders) throws IOException {
		if (!path.exists() || !path.isDirectory())
			throw new IOException(path.toString() + " do not exist!");

		File[] files = path.listFiles(filter);
		if (files == null)
			return;

		for (int i = 0; i < files.length; i++) {

			if (files[i].isDirectory() && includeSubfolders) {
				searchFiles(fileList, files[i], filter, true);
			} else {

				fileList.add(files[i]);
			}
		}
	}

	public static boolean rename(String oldFileName, String newFileName, boolean del) throws Exception {
		File inputFile = new File(oldFileName);
		File outputFile = new File(newFileName);
		try {
			if (!inputFile.exists()) {
				throw new Exception(oldFileName + " not exist ");
			}
			if (outputFile.exists()) {
				if (del) {
					outputFile.delete();
				} else {
					throw new Exception(newFileName + " already exist ");
				}
			}
			return inputFile.renameTo(outputFile);
		} catch (Exception ex) {
			throw new Exception("Can not rename" + oldFileName + " to " + newFileName + ex.getMessage());
		}
	}

	public static boolean rename(String oldFileName, String newFileName) throws Exception {
		return rename(oldFileName, newFileName, true);
	}

	public static void writeString(String fileName, String s) throws IOException {
		writeString(null, fileName, s);
	}

	public static void writeString(String path, String fileName, String s, String outFileCharsetName) throws IOException {
		
		if (UtilValidate.isNotEmpty(path)) {
			path = path.replaceAll("\\\\", "/");
			File parentDir = new File(path);
			if (!parentDir.exists()) {
				if (!parentDir.mkdir()) {
					throw new IOException("Cannot create directory for path: " + path);
				}
			}

			// build the filename with the path
			if (!path.endsWith("/")) {
				path = path + "/";
			}
			if (fileName.startsWith(File.separator)) {
				fileName = fileName.substring(1);
			}
			fileName = path + fileName;
		}

		if (UtilValidate.isNotEmpty(fileName)) {
			FileOutputStream fo = null;
			FileChannel fc = null;
			try {
				Charset outCharset;

				outCharset = Charset.forName(outFileCharsetName);

				ByteBuffer buff2 = outCharset.encode(s);

				fo = new FileOutputStream(fileName);
				fc = fo.getChannel();
				fc.write(buff2);

			} catch (IOException e) {
				log.error(e.toString());
				throw e;
			} finally {
				UtilMisc.closeObjNoExc(fc);
				UtilMisc.closeObjNoExc(fo);
			}
		} else {
			log.error("fileName is null");
			throw new IOException("Cannot write empty file name " + fileName);
		}
	}

	public static void writeString(String path, String fileName, String s) throws IOException {
		writeString(path, fileName, s, Default_File_Encoding);

	}

	/**
	 * 写入流,会关闭流
	 * 
	 * @param in
	 * @param out
	 * @throws IOException
	 */
	public static void copy(byte[] in, OutputStream out) throws IOException {
		assert (in != null);
		assert (out != null);
		try {
			out.write(in);
			// out.flush();
		} finally {

			out.close();

		}
	}

	public final static void copyNoClose(InputStream in, OutputStream out) throws IOException {
		byte[] buffer = new byte[BufferSize << 1];
		int bytesRead = -1;
		while ((bytesRead = in.read(buffer)) != -1) {
			out.write(buffer, 0, bytesRead);

		}
		out.flush();
	}

	public static final void saveFileByRandom(InputStream in, String filename, long length) throws IOException {
		RandomAccessFile rafo = new RandomAccessFile(filename, "rw");

		byte[] buf = new byte[BufferSize << 1];
		int read = -1, readonce = buf.length;
		long rest = length;
		try {
			while (true) {

				if (readonce > rest)
					readonce = (int) rest;
				read = in.read(buf, 0, readonce);

				if (read == -1) {
					break;
				}
				rest -= read;

				rafo.write(buf, 0, read);
				if (rest <= 0) {

					break;
				}
			}

		} finally {

			rafo.close();

		}

	}

	/**
	 * 内存影射文件方式,注意不能立即删除
	 * 
	 * @param in
	 * @param filename
	 * @param length
	 * @throws IOException
	 */
	public static final void saveFileBymap(InputStream in, String filename, long length) throws IOException {
		RandomAccessFile rafo = new RandomAccessFile(filename, "rw");
		FileChannel fco = rafo.getChannel();
		MappedByteBuffer mbbo = fco.map(FileChannel.MapMode.READ_WRITE, 0, length);

		byte[] buf = new byte[BufferSize << 1];
		int read = -1, readonce = buf.length;
		long rest = length;
		try {
			while (true) {

				if (readonce > rest)
					readonce = (int) rest;
				read = in.read(buf, 0, readonce);

				if (read == -1) {
					break;
				}
				rest -= read;

				mbbo.put(buf, 0, read);
				if (rest <= 0) {

					break;
				}
			}
			mbbo.force();
		} finally {
			UtilMisc.closeObjNoExc(fco);
			UtilMisc.closeObjNoExc(rafo);
		}

	}

	/**
	 * 复制流,不会关闭流
	 * 
	 * @param in
	 * @param out
	 * @param length
	 * @throws IOException
	 */
	public static final void copy(InputStream in, OutputStream out, long length) throws IOException {
		byte[] buf = new byte[BufferSize << 1];
		int read = -1, readonce = buf.length;
		long rest = length;

		while (true) {

			if (readonce > rest)
				readonce = (int) rest;
			read = in.read(buf, 0, readonce);

			if (read == -1) {
				break;
			}
			rest -= read;
			out.write(buf, 0, read);

			if (rest <= 0) {

				break;
			}
		}
		out.flush();

	}

	/**
	 * 复制流,会关闭流
	 * 
	 * @param in
	 * @param out
	 * @return
	 * @throws IOException
	 */
	public static final long copy(InputStream in, OutputStream out) throws IOException {
		assert (in != null);
		assert (out != null);
		try {
			return copyStreamNoClose(in, out);

		} finally {
			try {
				in.close();
			} catch (IOException ex) {
				log.warn("InputStream close error", ex);
			}
			try {
				out.close();
			} catch (IOException ex) {
				log.warn("outputStream close error", ex);
			}
		}
	}

	public static final long copyStreamNoClose(InputStream in, OutputStream out) throws IOException {

		long byteCount = 0;
		byte[] buffer = new byte[BufferSize];
		int bytesRead = -1;
		while ((bytesRead = in.read(buffer)) != -1) {
			out.write(buffer, 0, bytesRead);
			byteCount += bytesRead;
		}
		out.flush();
		return byteCount;

	}

	/**
	 * 通过国write输出,输出完毕Writer 关闭
	 * 
	 * @param in
	 * @param out
	 * @throws IOException
	 */
	public static void copy(String in, Writer out) throws IOException {
		assert (in != null);
		assert (out != null);
		try {
			out.write(in);
		} finally {

			out.close();

		}
	}

	/**
	 * 得到临时目录，一定带有路径分割
	 * 
	 * @return
	 */
	public static final String getTempDir() {
		String tempdir = System.getProperty("java.io.tmpdir");
		String temPath;
		if (!tempdir.endsWith(File.separator))
			temPath = tempdir + File.separator;
		else
			temPath = tempdir;
		return temPath;
	}

	/**
	 * 得到用户目录，一定带有路径分割
	 * 
	 * @return
	 */
	public static final String getUserDir() {
		String tempdir = System.getProperty("user.dir");
		String temPath;
		if (!tempdir.endsWith(File.separator))
			temPath = tempdir + File.separator;
		else
			temPath = tempdir;
		return temPath;
	}

	public static final String appendPathSeparator(String path) {
		String temPath = null;
		if (path != null) {
			if (!path.endsWith(File.separator))
				temPath = path.concat(File.separator);
			else
				temPath = path;
		}

		return temPath;
	}

	/**
	 * 向文件追加数据
	 * 
	 * @param file
	 * @param b
	 * @throws IOException
	 */
	public static final boolean appentToFile(File file, byte[] b) throws IOException {
		return appentToFile(file, b, 0, b.length);
	}

	/**
	 * 向文件追加数据 会锁定文件,注意,同一jvm抛异常
	 * 
	 * @param file
	 * @param b
	 * @throws IOException
	 */
	public static final boolean appentToFile(File file, byte[] b, int off, int len) throws IOException {
		FileOutputStream rf = new FileOutputStream(file, true);

		try {

			FileChannel fc = rf.getChannel();
			FileLock fl = fc.tryLock(0L, Long.MAX_VALUE, false);// 阻止读取
			if (fl != null) {
				try {
					rf.write(b, off, len);
				} finally {
					fl.release();
				}

				return true;
			} else {
				log.error("lock file {} fail", file);
				return false;
			}

		} catch (IOException e) {
			throw e;
		} finally {
			rf.close();// 关闭文件流
		}

	}

	public static final void appentData(File file, byte[] b, int off, int len) throws IOException {
		RandomAccessFile rf = new RandomAccessFile(file, "rwd");

		try {
			rf.seek(file.length());

			rf.write(b, off, len);

		} catch (IOException e) {
			throw e;
		} finally {
			rf.close();// 关闭文件
		}

	}

	/**
	 * 传递流的文件写,不会关闭流
	 * 
	 * @param rf
	 * @param b
	 * @param off
	 * @param len
	 * @return
	 * @throws IOException
	 */
	public static final boolean appentToFile(FileOutputStream rf, byte[] b, int off, int len) throws IOException {

		try {

			FileChannel fc = rf.getChannel();
			FileLock fl = fc.tryLock(0L, Long.MAX_VALUE, false);// 阻止读取
			if (fl != null) {
				try {
					rf.write(b, off, len);
				} finally {
					fl.release();
				}

				return true;
			} else {
				log.error("lock file {} fail", rf.toString());
				return false;
			}

		} catch (IOException e) {
			throw e;
		}

	}

	public static final boolean appentToFile(File file, final String str, final String encode) throws IOException {
		if (UtilValidate.isEmpty(str))
			return false;
		byte[] b;
		final String lineSep = System.getProperty("line.separator");
		if (encode == null)
			b = (str + lineSep).getBytes();
		else
			b = (str + lineSep).getBytes(encode);
		return appentToFile(file, b);
	}

	/**
	 * 按行读取
	 * 
	 * @param in
	 * @param comm
	 *            注释符号
	 * @param charset
	 *            文件编码
	 * @return 文件内容
	 * @throws IOException
	 */
	public static final List<String> readLine(InputStream in, String comm, String charset) throws IOException {
		if (UtilValidate.isEmpty(charset)) {
			charset = Charset_UTF8;
		}
		InputStreamReader ir = new InputStreamReader(in, charset);
		BufferedReader br = new BufferedReader(ir);
		List<String> result = new ArrayList<String>(500);
		String line;
		try {

			while ((line = br.readLine()) != null) {
				if (!UtilValidate.isWhitespace(line)) {
					if (UtilValidate.isEmpty(comm)) {
						result.add(line);
					} else if (line.startsWith(comm)) {
						continue;
					} else {
						result.add(line);
					}

				}

			}
		} finally {
			br.close();

		}

		return result;
	}

	public final static void writeString(OutputStream out, final Charset charset, final String value) throws IOException {
		Writer writer = new OutputStreamWriter(out, charset);
		final String nl = System.getProperty("line.separator");
		try {
			int r = 0;
			while (r < value.length()) {
				int i = value.indexOf('\n', r);
				if (i == -1) {
					break;
				}
				writer.write(value.substring(r, i));
				writer.write(nl);
				r = i + 1;
			}
			writer.write(value.substring(r));
		} finally {
			writer.close();
		}

	}

	public final static void writeString(OutputStream out, String value) throws IOException {
		writeString(out, StandardCharsets.UTF_8, value);
	}

	public final static void writeString(File file, String value) throws IOException {
		writeString(new FileOutputStream(file), StandardCharsets.UTF_8, value);
	}

	public static final String readFileToString(File fr, Charset charset) throws IOException {
		return readString(new InputStreamReader(new FileInputStream(fr), charset));
	}

	public static final String readString(InputStream stream, Charset charset) throws IOException {
		return readString(new InputStreamReader(stream, charset));
	}

	/**
	 * 在reader中读取信息，
	 * 
	 * @param reader
	 *            会被关闭
	 * @return
	 * @throws IOException
	 */
	public static final String readString(Reader reader) throws IOException {
		try {
			StringBuilder sb = new StringBuilder(BufferSize);
			char[] buf = new char[BufferSize];
			int r;
			while ((r = reader.read(buf, 0, BufferSize)) > 0) {
				sb.append(buf, 0, r);
			}
			return sb.toString();
		} finally {
			UtilMisc.closeObjNoExc(reader);
		}
	}

}
